#pragma once

#define MQUERY(t, x, q) ref new mbase::Query##t(#t, #x, ([&](mbase::VariableExpression##t^ x) { return mbase::Query::expression(q); })(ref new mbase::VariableExpression##t(#x)), ref new mbase::QueryLambda([&](MetroClass^ _mobj_)->bool { t^ x = (t^) _mobj_; return q; }))

#define MIDQUERY(t, x) ref new mbase::Query##t(#t, x)

#define MSTRING(cls, name) partial ref class cls { public: property String^ name { String^ get() { return strProps->HasKey(#name) ? strProps->Lookup(#name) : nullptr; } void set(String^ obj) { WillChangeProperty(#name); if (obj != nullptr) strProps->Insert(#name, obj); else { if (strProps->HasKey(#name)) strProps->Remove(#name); } DidChangeProperty(#name); } } };\
	namespace mbase { partial ref class VariableExpression##cls { public: property mbase::StrPropertyExpression^ name { mbase::StrPropertyExpression^ get() { return ref new mbase::StrPropertyExpression(this, #name); } } }; }

//#define MNUMBER(cls, name) partial ref class cls { public: property Number^ name { Number^ get() { return numProps->Lookup(#name); } void set(Number^ n) { numProps->Insert(#name, n); } } };
#define MNUMBER(cls, name) partial ref class cls { public: property int name { int get() { return numProps->Lookup(#name)->N; } void set(int n) { WillChangeProperty(#name); numProps->Insert(#name, ref new Number(n)); DidChangeProperty(#name); } } };\
	namespace mbase { partial ref class VariableExpression##cls { public: property mbase::NumPropertyExpression^ name { mbase::NumPropertyExpression^ get() { return ref new mbase::NumPropertyExpression(this, #name); } } }; }

#define MFLOAT(cls, name) partial ref class cls { public: property float name { float get() { return fltProps->Lookup(#name)->N; } void set(float n) { WillChangeProperty(#name); fltProps->Insert(#name, ref new RealNumber(n)); DidChangeProperty(#name); } } };\
	namespace mbase { partial ref class VariableExpression##cls { public: property mbase::FltPropertyExpression^ name { mbase::FltPropertyExpression^ get() { return ref new mbase::FltPropertyExpression(this, #name); } } }; }

#define MOBJECT(cls, type, name) partial ref class cls { public: property type^ name { type^ get() { return objProps->HasKey(#name) ? (type^) objProps->Lookup(#name) : nullptr; } void set(type^ o) { WillChangeProperty(#name); if (o == nullptr) { if (objProps->HasKey(#name)) objProps->Remove(#name); } else objProps->Insert(#name, o); DidChangeProperty(#name); } } };\
	namespace mbase { partial ref class VariableExpression##cls { public: property mbase::PropertyExpression##type^ name { mbase::PropertyExpression##type^ get() { return ref new mbase::PropertyExpression##type(this, #name); } } }; }

#define MGENOBJECT(cls, name) partial ref class cls { public: property MetroClass^ name { MetroClass^ get() { return objProps->HasKey(#name) ? objProps->Lookup(#name) : nullptr; } void set(MetroClass^ o) { WillChangeProperty(#name); if (o == nullptr) { if (objProps->HasKey(#name)) objProps->Remove(#name); } else objProps->Insert(#name, o); DidChangeProperty(#name); } } };
//	namespace mbase { partial ref class VariableExpression##cls { public: property mbase::PropertyExpression^ name { mbase::PropertyExpression^ get() { return ref new mbase::PropertyExpression(this, #name); } } }; }

#define MCLASS(x) partial ref class x {};
//	namespace mbase { partial ref class VariableExpression##cls {}; }

#define MENDCLASS(x) ref class x sealed : mbase::MetroClass { public: x() : MetroClass() {} x(mbase::UniqueID^ uid) : MetroClass(uid) {} };\
	namespace mbase { ref class VariableExpression##x : VariableExpression { public: mbase::VariableExpression##x(String^ v) : VariableExpression(v) {} }; }\
	namespace mbase { ref class PropertyExpression##x : VariableExpression##x { private: mbase::Expression^ expr; public: PropertyExpression##x(mbase::Expression^ e, String^ p) : expr(e), mbase::VariableExpression##x(p) {}\
	  Value^ evaluate(IMap<String^,Value^>^ vars) {\
		return ref new mbase::ObjectValue(expr->evaluate(vars)->asObject()->GetObjProperty(GetVarName()));\
	  }};}\
	namespace mbase { ref class Query##x sealed : Query { public: MetroClass^ createObject(String^ uid) { return ref new x(ref new mbase::UniqueID(uid)); } Query##x(String^ c, String^ v, Expression^ e, QueryLambda^ l) : Query(c, v, e, l) {} Query##x(String^ c, String^ uid) : Query(c, uid) {} }; }\
	namespace mbase { ref class Init##x { public: Init##x() { Database::SetCreateQueryBlock(#x, ref new CreateQueryBlock([](String^ uid) { return ref new Query##x(#x, uid); })); Database::SetCreateObjectBlock(#x, ref new CreateObjectBlock([](String^ uid) { return ref new x(ref new mbase::UniqueID(uid)); })); /*OutputDebugStringW((L"Init: " + #x + "\n")->Data());*/ } }; static auto _init##x = ref new Init##x; }
